1 <?php
2     
if(!defined('datalist_db_encoding')) define('datalist_db_encoding', 'UTF-8');
3     
if(function_exists('date_default_timezone_set')) @date_default_timezone_set('America/New_York');
4
5     
/* force caching */
6     $last_modified = filemtime(__FILE__);
7     $last_modified_gmt = gmdate(
'D, d M Y H:i:s', $last_modified) . ' GMT';
8     $headers = (function_exists(
'getallheaders') ? getallheaders() : $_SERVER);
9     
if(isset($headers['If-Modified-Since']) && (strtotime($headers['If-Modified-Since']) == $last_modified)){
10         @header(
"Last-Modified: {$last_modified_gmt}", true, 304);
11         @header(
"Cache-Control: public, max-age=240", true);
12         exit;
13     }
14
15     @header(
"Last-Modified: {$last_modified_gmt}", true, 200);
16     @header(
"Cache-Control: public, max-age=240", true);
17     @header(
'Content-Type: text/javascript; charset=' . datalist_db_encoding);
18     $currDir = dirname(__FILE__);
19     include(
"{$currDir}/defaultLang.php");
20     include(
"{$currDir}/language.php");
21 ?>

22 var
AppGini = AppGini || {};
23 AppGini.ajaxCache = function(){
24     
var _tests = [];
25
26     
/*
27         An array of functions that receive a parameterless url and a parameters
object,
28         makes a test,
29         and
if test passes, executes something and/or
30         returns a non-
false value if test passes,
31         or
false if test failed (useful to tell if tests should continue or not)
32     */

33     
var addCheck = function(check){ /* */
34         
if(typeof(check) == 'function'){
35             _tests.push(check);
36         }
37     };
38
39     
var _jqAjaxData = function(opt){ /* */
40         
var opt = opt || {};
41         
var url = opt.url || '';
42         
var data = opt.data || {};
43
44         
var params = url.match(/\?(.*)$/);
45         
var param = (params !== null ? params[1] : '');
46
47         
var sPageURL = decodeURIComponent(param),
48             sURLVariables = sPageURL.split(
'&'),
49             sParameter,
50             i;
51
52         
for(i = 0; i < sURLVariables.length; i++){
53             sParameter = sURLVariables[i].split(
'=');
54             
if(sParameter[0] == '') continue;
55             data[sParameter[
0]] = sParameter[1] || '';
56         }
57
58         
return data;
59     };
60
61     
var start = function(){ /* */
62         
if(!_tests.length) return; // no need to monitor ajax requests since no checks were defined
63         
var reqTests = _tests;
64         $j.ajaxPrefilter(function(options, originalOptions, jqXHR){
65             
var success = originalOptions.success || $j.noop,
66                 data = _jqAjaxData(originalOptions),
67                 oUrl = originalOptions.url ||
'',
68                 url = oUrl.match(/\?/) ? oUrl.match(/(.*)\?/)[
1] : oUrl;
69
70             options.beforeSend = function(){
/* */
71                 
var req, cached = false, resp;
72
73                 
for(var i = 0; i < reqTests.length; i++){
74                     resp = reqTests[i](url, data);
75                     
if(resp === false) continue;
76
77                     success(resp);
78                     
return false;
79                 }
80
81                 
return true;
82             }
83         });
84     };
85
86     
return {
87         addCheck: addCheck,
88         start: start
89     };
90 };

91
92 /* initials and fixes */

93 jQuery(function(){
94     AppGini.count_ajaxes_blocking_saving =
0;
95
96     
/* add ":truncated" pseudo-class to detect elements with clipped text */
97     $j.expr[
':'].truncated = function(obj){
98         
var $this = $j(obj);
99         
var $c = $this
100                     .clone()
101                     .css({ display:
'inline', width: 'auto', visibility: 'hidden', 'padding-right': 0 })
102                     .css({
'font-size': $this.css('font-size') })
103                     .appendTo(
'body');
104
105         
var e_width = $this.outerWidth();
106         
var c_width = $c.outerWidth();
107         $c.
remove();
108
109         
return ( c_width > e_width );
110     };
111
112     
var fix_lookup_width = function(field){
113         
var s2 = $j('div.select2-container[id=s2id_' + field + '-container]');
114         
if(!s2.length) return;
115
116         
var s2new_width = 0, s2view_width = 0, s2parent_width = 0;
117
118         
var s2new = s2.parent().find('.add_new_parent:visible');
119         
var s2view = s2.parent().find('.view_parent:visible');
120         
if(s2new.length) s2new_width = s2new.outerWidth(true);
121         
if(s2view.length) s2view_width = s2view.outerWidth(true);
122         s2parent_width = s2.parent().innerWidth();
123
124         
// console.log({ s2new_width: s2new_width, s2view_width: s2view_width, s2parent_width: s2parent_width });
125
126         s2.css({ width:
'100%', 'max-width': (s2parent_width - s2new_width - s2view_width - 1) + 'px' });
127     }
128
129     $j(window).resize(function(){
130         
var window_width = $j(window).width();
131         
var max_width = $j('body').width() * 0.5;
132
133         $j(
'.select2-container:not(.option_list)').each(function(){
134             
var field = $j(this).attr('id').replace(/^s2id_/, '').replace(/-container$/, '');
135             fix_lookup_width(field);
136         });
137
138         
//fix_table_responsive_width();
139
140         
var full_img_factor = 0.9; /* xs */
141         
if(window_width >= 992) full_img_factor = 0.6; /* md, lg */
142         
else if(window_width >= 768) full_img_factor = 0.9; /* sm */
143
144         $j(
'.detail_view .img-responsive').css({'max-width' : parseInt($j('.detail_view').width() * full_img_factor) + 'px'});
145
146         
/* remove labels from truncated buttons, leaving only glyphicons */
147         $j(
'.btn.truncate:truncated').each(function(){
148             // hide text
149             
var label = $j(this).html();
150             
var mlabel = label.replace(/.*(<i.*?><\/i>).*/, '$1');
151             $j(
this).html(mlabel);
152         });
153     });
154
155     setTimeout(function(){
/* */ $j(window).resize(); }, 1000);
156     setTimeout(function(){
/* */ $j(window).resize(); }, 3000);
157
158     
/* don't allow saving detail view when there's an ajax request to a url that matches the following */
159     
var ajax_blockers = new RegExp(/(ajax_combo\.php|_autofill\.php|ajax_check_unique\.php)/);
160     $j(document).ajaxSend(function(e, r, s){
161         
if(s.url.match(ajax_blockers)){
162             AppGini.count_ajaxes_blocking_saving++;
163             $j(
'#update, #insert').prop('disabled', true);
164         }
165     });
166     $j(document).ajaxComplete(function(e, r, s){
167         
if(s.url.match(ajax_blockers)){
168             AppGini.count_ajaxes_blocking_saving = Math.max(AppGini.count_ajaxes_blocking_saving -
1, 0);
169             
if(AppGini.count_ajaxes_blocking_saving <= 0)
170                 $j(
'#update, #insert').prop('disabled', false);
171         }
172     });
173
174     
/* don't allow responsive images to initially exceed the smaller of their actual dimensions, or .6 container width */
175     jQuery(
'.detail_view .img-responsive').each(function(){
176          
var pic_real_width, pic_real_height;
177          
var img = jQuery(this);
178          jQuery(
'<img/>') // Make in memory copy of image to avoid css issues
179                 .attr(
'src', img.attr('src'))
180                 .load(function() {
181                     pic_real_width =
this.width;
182                     pic_real_height =
this.height;
183
184                     
if(pic_real_width > $j('.detail_view').width() * .6) pic_real_width = $j('.detail_view').width() * .6;
185                     img.css({
"max-width": pic_real_width });
186                 });
187     });
188
189     jQuery(
'.table-responsive .img-responsive').each(function(){
190          
var pic_real_width, pic_real_height;
191          
var img = jQuery(this);
192          jQuery(
'<img/>') // Make in memory copy of image to avoid css issues
193                 .attr(
'src', img.attr('src'))
194                 .load(function() {
195                     pic_real_width =
this.width;
196                     pic_real_height =
this.height;
197
198                     
if(pic_real_width > $j('.table-responsive').width() * .6) pic_real_width = $j('.table-responsive').width() * .6;
199                     img.css({
"max-width": pic_real_width });
200                 });
201     });
202
203     
/* toggle TV action buttons based on selected records */
204     jQuery(
'.record_selector').click(function(){
205         
var id = jQuery(this).val();
206         
var checked = jQuery(this).prop('checked');
207         update_action_buttons();
208     });
209
210     
/* select/deselect all records in TV */
211     jQuery(
'#select_all_records').click(function(){
212         jQuery(
'.record_selector').prop('checked', jQuery(this).prop('checked'));
213         update_action_buttons();
214     });
215
216     
/* fix behavior of select2 in bootstrap modal. See: https://github.com/ivaynberg/select2/issues/1436 */
217     jQuery.fn.modal.Constructor.prototype.enforceFocus = function(){
/* */ };
218
219     
/* remove empty navbar menus */
220     $j(
'nav li.dropdown').each(function(){
221         
var num_items = $j(this).children('.dropdown-menu').children('li').length;
222         
if(!num_items) $j(this).remove();
223     })
224
225     update_action_buttons();
226
227     
/* remove empty images and links from TV, TVP */
228     $j(
'.table a[href="<?php echo $Translation['ImageFolder']; ?>"], .table img[src="<?php echo $Translation['ImageFolder']; ?>"]').remove();
229
230     
/* remove empty email links from TV, TVP */
231     $j(
'a[href="mailto:"]').remove();
232
233     
/* Disable action buttons when form is submitted to avoid user re-submission on slow connections */
234     $j(
'form').eq(0).submit(function(){
235         setTimeout(function(){
236             $j(
'#insert, #update, #delete, #deselect').prop('disabled', true);
237         },
200); // delay purpose is to allow submitting the button values first then disable them.
238     });
239
240     
/* fix links inside alerts */
241     $j(
'.alert a:not(.btn)').addClass('alert-link');
242 });

243
244 /* show/hide TV action buttons based
on whether records are selected or not */
245 function update_action_buttons(){
246     
if(jQuery('.record_selector:checked').length){
247         jQuery(
'.selected_records').removeClass('hidden');
248         jQuery(
'#select_all_records')
249             .prop(
'checked', (jQuery('.record_selector:checked').length == jQuery('.record_selector').length));
250     }
else{
251         jQuery(
'.selected_records').addClass('hidden');
252     }
253 }

254
255 /* fix table-responsive behavior
on Chrome */
256 function fix_table_responsive_width(){
257     
var resp_width = jQuery('div.table-responsive').width();
258     
var table_width;
259
260     
if(resp_width){
261         jQuery(
'div.table-responsive table').width('100%');
262         table_width = jQuery(
'div.table-responsive table').width();
263         resp_width = jQuery(
'div.table-responsive').width();
264         
if(resp_width == table_width){
265             jQuery(
'div.table-responsive table').width(resp_width - 1);
266         }
267     }
268 }
269
270 function schools_validateData(){
271     $j(
'.has-error').removeClass('has-error');
272     
/* Field name can't be empty */
273     
if($j('#name').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Name", close: function(){ /* */ $j('[name=name]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
274     
return true;
275 }
276 function departments_validateData(){
277     $j(
'.has-error').removeClass('has-error');
278     
/* Field name can't be empty */
279     
if($j('#name').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Name", close: function(){ /* */ $j('[name=name]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
280     
/* Field school can't be empty */
281     
if($j('#school').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> School", close: function(){ /* */ $j('[name=school]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
282     
return true;
283 }
284 function class_time_table_validateData(){
285     $j(
'.has-error').removeClass('has-error');
286     
/* Field day can't be empty */
287     
if($j('#day').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Day", close: function(){ /* */ $j('[name=day]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
288     
/* Field time_start can't be empty */
289     
if($j('#time_start').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Time Start", close: function(){ /* */ $j('[name=time_start]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
290     
/* Field time_end can't be empty */
291     
if($j('#time_end').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Time End", close: function(){ /* */ $j('[name=time_end]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
292     
/* Field unit_code can't be empty */
293     
if($j('#unit_code').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Unit code", close: function(){ /* */ $j('[name=unit_code]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
294     
/* Field venue can't be empty */
295     
if($j('#venue').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Venue", close: function(){ /* */ $j('[name=venue]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
296     
/* Field school can't be empty */
297     
if($j('#school').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> School", close: function(){ /* */ $j('[name=school]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
298     
/* Field department can't be empty */
299     
if($j('#department').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Department", close: function(){ /* */ $j('[name=department]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
300     
/* Field year_of_study can't be empty */
301     
if($j('#year_of_study').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Year of study", close: function(){ /* */ $j('[name=year_of_study]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
302     
return true;
303 }
304 function exam_time_table_validateData(){
305     $j(
'.has-error').removeClass('has-error');
306     
/* Date field date can't be empty */
307     
if($j('#date-dd').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Date", close: function(){ /* */ $j('#date-dd').focus().parents('.form-group').addClass('has-error'); } }); return false; };
308     
if($j('#date-mm').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Date", close: function(){ /* */ $j('#date-mm').focus().parents('.form-group').addClass('has-error'); } }); return false; };
309     
if($j('#date').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Date", close: function(){ /* */ $j('#date').focus().parents('.form-group').addClass('has-error'); } }); return false; };
310     
/* Field time_start can't be empty */
311     
if($j('#time_start').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Time Start", close: function(){ /* */ $j('[name=time_start]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
312     
/* Field time_end can't be empty */
313     
if($j('#time_end').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Time End", close: function(){ /* */ $j('[name=time_end]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
314     
/* Field unit_code can't be empty */
315     
if($j('#unit_code').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Unit code", close: function(){ /* */ $j('[name=unit_code]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
316     
/* Field venue can't be empty */
317     
if($j('#venue').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Venue", close: function(){ /* */ $j('[name=venue]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
318     
/* Field school can't be empty */
319     
if($j('#school').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> School", close: function(){ /* */ $j('[name=school]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
320     
/* Field department can't be empty */
321     
if($j('#department').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Department", close: function(){ /* */ $j('[name=department]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
322     
/* Field year_of_study can't be empty */
323     
if($j('#year_of_study').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Year of study", close: function(){ /* */ $j('[name=year_of_study]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
324     
return true;
325 }
326 function personal_time_table_validateData(){
327     $j(
'.has-error').removeClass('has-error');
328     
/* Field day can't be empty */
329     
if($j('#day').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Day", close: function(){ /* */ $j('[name=day]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
330     
/* Field time_start can't be empty */
331     
if($j('#time_start').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Time Start", close: function(){ /* */ $j('[name=time_start]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
332     
/* Field time_end can't be empty */
333     
if($j('#time_end').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Time End", close: function(){ /* */ $j('[name=time_end]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
334     
/* Field activity can't be empty */
335     
if($j('#activity').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Activity", close: function(){ /* */ $j('[name=activity]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
336     
return true;
337 }
338 function student_details_validateData(){
339     $j(
'.has-error').removeClass('has-error');
340     
/* Field full_name can't be empty */
341     
if($j('#full_name').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Full name", close: function(){ /* */ $j('[name=full_name]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
342     
/* Field school can't be empty */
343     
if($j('#school').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> School", close: function(){ /* */ $j('[name=school]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
344     
/* Field department can't be empty */
345     
if($j('#department').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Department", close: function(){ /* */ $j('[name=department]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
346     
/* Field year_of_study can't be empty */
347     
if($j('#year_of_study').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Year of study", close: function(){ /* */ $j('[name=year_of_study]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
348     
/* Field reg_no can't be empty */
349     
if($j('#reg_no').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Reg no", close: function(){ /* */ $j('[name=reg_no]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
350     
return true;
351 }
352 function notices_validateData(){
353     $j(
'.has-error').removeClass('has-error');
354     
/* Field notice can't be empty */
355     
if($j('#notice').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Notice", close: function(){ /* */ $j('[name=notice]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
356     
/* Field school can't be empty */
357     
if($j('#school').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> School", close: function(){ /* */ $j('[name=school]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
358     
/* Field department can't be empty */
359     
if($j('#department').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Department", close: function(){ /* */ $j('[name=department]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
360     
/* Field year_of_study can't be empty */
361     
if($j('#year_of_study').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Year of study", close: function(){ /* */ $j('[name=year_of_study]').eq(0).focus().parents('.form-group').addClass('has-error'); }, footer: [{ label: '<?php echo addslashes($Translation['ok']); ?>' }] }); return false; };
362     
return true;
363 }
364
365 function post(url,
params, update, disable, loading, success_callback){
366     $j.ajax({
367         url: url,
368         type:
'POST',
369         data:
params,
370         beforeSend: function() {
371             
if($j('#' + disable).length) $j('#' + disable).prop('disabled', true);
372             
if($j('#' + loading).length && update != loading) $j('#' + loading).html('<div style="direction: ltr;"><img src="loading.gif"> <?php echo addslashes($Translation['Loading ...']); ?></div>');
373         },
374         success: function(resp) {
375             
if($j('#' + update).length) $j('#' + update).html(resp);
376             
if(success_callback != undefined) success_callback();
377         },
378         complete: function() {
379             
if($j('#' + disable).length) $j('#' + disable).prop('disabled', false);
380             
if($j('#' + loading).length && loading != update) $j('#' + loading).html('');
381         }
382     });
383 }
384
385 function post2(url,
params, notify, disable, loading, redirectOnSuccess){
386     
new Ajax.Request(
387         url, {
388             method:
'post',
389             parameters:
params,
390             onCreate: function() {
391                 
if($(disable) != undefined) $(disable).disabled=true;
392                 
if($(loading) != undefined) $(loading).show();
393             },
394             onSuccess: function(resp) {
395                 
/* show notification containing returned text */
396                 
if($(notify) != undefined) $(notify).removeClassName('Error').appear().update(resp.responseText);
397
398                 
/* in case no errors returned, */
399                 
if(!resp.responseText.match(/<?php echo $Translation['error:']; ?>/)){
400                     
/* redirect to provided url */
401                     
if(redirectOnSuccess != undefined){
402                         window.location=redirectOnSuccess;
403
404                     
/* or hide notification after a few seconds if no url is provided */
405                     }
else{
406                         
if($(notify) != undefined) window.setTimeout(function(){ /* */ $(notify).fade(); }, 15000);
407                     }
408
409                 
/* in case of error, apply error class */
410                 }
else{
411                     $(notify).addClassName(
'Error');
412                 }
413             },
414             onComplete: function() {
415                 
if($(disable) != undefined) $(disable).disabled=false;
416                 
if($(loading) != undefined) $(loading).hide();
417             }
418         }
419     );
420 }
421 function passwordStrength(password, username){
422     
// score calculation (out of 10)
423     
var score = 0;
424     re =
new RegExp(username, 'i');
425     
if(username.length && password.match(re)) score -= 5;
426     
if(password.length < 6) score -= 3;
427     
else if(password.length > 8) score += 5;
428     
else score += 3;
429     
if(password.match(/(.*[0-9].*[0-9].*[0-9])/)) score += 3;
430     
if(password.match(/(.*[!,@,#,$,%,^,&,*,?,_,~].*[!,@,#,$,%,^,&,*,?,_,~])/)) score += 5;
431     
if(password.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/)) score += 2;
432
433     
if(score >= 9)
434         
return 'strong';
435     
else if(score >= 5)
436         
return 'good';
437     
else
438         
return 'weak';
439 }
440 function validateEmail(email) {
441     
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
442     
return re.test(email);
443 }
444 function loadScript(jsUrl, cssUrl, callback){
445     
// adding the script tag to the head
446     
var head = document.getElementsByTagName('head')[0];
447     
var script = document.createElement('script');
448     script.type = 'text/javascript';
449     script.src = jsUrl;
450
451     
if(cssUrl != ''){
452         
var css = document.createElement('link');
453         css.href = cssUrl;
454         css.rel = "
stylesheet";
455         css.type = "
text/css";
456         head.appendChild(css);
457     }
458
459     
// then bind the event to the callback function
460     
// there are several events for cross browser compatibility
461     
if(script.onreadystatechange != undefined){ script.onreadystatechange = callback; }
462     
if(script.onload != undefined){ script.onload = callback; }
463
464     
// fire the loading
465     head.appendChild(script);
466 }

467 /**
468  * options
object. The following members can be provided:
469  * url: iframe url to load
470  * message: instead of a url to open, you could pass a message. HTML tags allowed.
471  * id: id attribute of modal window. auto-generated
if not provided
472  * title: optional modal window title
473  * size: '
default', 'full'
474  * close: optional function to execute
on closing the modal
475  * footer: optional array of objects describing the buttons to display
in the footer.
476  * Each button
object can have the following members:
477  * label:
string, label of button
478  * bs_class:
string, button bootstrap class. Can be 'primary', 'default', 'success', 'warning' or 'danger'
479  * click: function to execute
on clicking the button. If the button closes the modal, this
480  * function
is executed before the close handler
481  * causes_closing: boolean,
default is true.
482  */

483 function modal_window(options){
484     
return jQuery('body').agModal(options).agModal('show').attr('id');
485 }
486
487 function random_string(string_length){
488     
var text = "";
489     
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
490
491     
for(var i = 0; i < string_length; i++)
492         text += possible.charAt(Math.floor(Math.random() * possible.length));
493
494     
return text;
495 }

496
497 /**
498  * @
return array of IDs (PK values) of selected records in TV (records that the user checked)
499  */

500 function get_selected_records_ids(){
501     
return jQuery('.record_selector:checked').map(function(){ /* */ return jQuery(this).val() }).get();
502 }
503
504 function print_multiple_dv_tvdv(t, ids){
505     document.myform.NoDV.
value=1;
506     document.myform.PrintDV.
value=1;
507     document.myform.SelectedID.
value = '';
508     document.myform.submit();
509     
return true;
510 }
511
512 function print_multiple_dv_sdv(t, ids){
513     document.myform.NoDV.
value=1;
514     document.myform.PrintDV.
value=1;
515     document.myform.writeAttribute('novalidate', 'novalidate');
516     document.myform.submit();
517     
return true;
518 }
519
520 function mass_delete(t, ids){
521     
if(ids == undefined) return;
522     
if(!ids.length) return;
523
524     
var confirm_message = '<div class="alert alert-danger">' +
525             '<i
class="glyphicon glyphicon-warning-sign"></i> ' +
526             '<?php echo addslashes($Translation['<n> records will be deleted. Are you sure you want to
do this?']); ?>' +
527         '</div>';
528     
var confirm_title = '<?php echo addslashes($Translation['Confirm deleting multiple records']); ?>';
529     
var label_yes = '<?php echo addslashes($Translation['Yes, delete them!']); ?>';
530     
var label_no = '<?php echo addslashes($Translation['No, keep them.']); ?>';
531     
var progress = '<?php echo addslashes($Translation['Deleting record <i> of <n>']); ?>';
532     
var continue_delete = true;
533
534     
// request confirmation of mass delete operation
535     modal_window({
536         message: confirm_message.replace(/\<n\>/, ids.length),
537         title: confirm_title,
538         footer: [
/* shows a 'yes' and a 'no' buttons .. handler for each follows ... */
539             {
540                 label: '<i
class="glyphicon glyphicon-trash"></i> ' + label_yes,
541                 bs_class: 'danger',
542                 
// on confirming, start delete operations
543                 click: function(){
544
545                     
// show delete progress, allowing user to abort operations by closing the window or clicking cancel
546                     
var progress_window = modal_window({
547                         title: '<?php echo addslashes($Translation['Delete progress']); ?>',
548                         message: '' +
549                             '<div
class="progress">' +
550                                 '<div
class="progress-bar progress-bar-warning" role="progressbar" style="width: 0;"></div>' +
551                             '</div>' +
552                             '<button type="
button" class="btn btn-default details_toggle" onclick="' +
553                                 
'jQuery(this).children(\'.glyphicon\').toggleClass(\'glyphicon-chevron-right glyphicon-chevron-down\'); ' +
554                                 
'jQuery(\'.well.details_list\').toggleClass(\'hidden\');'
555                                 +
'">' +
556                                 
'<i class="glyphicon glyphicon-chevron-right"></i> ' +
557                                 
'<?php echo addslashes($Translation['Show/hide details']); ?>' +
558                             
'</button>' +
559                             
'<div class="well well-sm details_list hidden"><ol></ol></div>',
560                         close: function(){
561                             
// stop deleting further records ...
562                             continue_delete =
false;
563                         },
564                         footer: [
565                             {
566                                 label:
'<i class="glyphicon glyphicon-remove"></i> <?php echo addslashes($Translation['Cancel']); ?>',
567                                 bs_class:
'warning'
568                             }
569                         ]
570                     });
571
572                     
// begin deleting records, one by one
573                     progress = progress.replace(/\<n\>/, ids.length);
574                     
var delete_record = function(itrn){
575                         
if(!continue_delete) return;
576                         jQuery.ajax(t +
'_view.php', {
577                             type:
'POST',
578                             data: { delete_x:
1, SelectedID: ids[itrn] },
579                             success: function(resp){
580                                 
if(resp == 'OK'){
581                                     jQuery(
".well.details_list ol").append('<li class="text-success"><?php echo addslashes($Translation['The record has been deleted successfully']); ?></li>');
582                                     jQuery(
'#record_selector_' + ids[itrn]).prop('checked', false).parent().parent().fadeOut(1500);
583                                     jQuery(
'#select_all_records').prop('checked', false);
584                                 }
else{
585                                     jQuery(
".well.details_list ol").append('<li class="text-danger">' + resp + '</li>');
586                                 }
587                             },
588                             error: function(){
589                                 jQuery(
".well.details_list ol").append('<li class="text-warning"><?php echo addslashes($Translation['Connection error']); ?></li>');
590                             },
591                             complete: function(){
592                                 jQuery(
'#' + progress_window + ' .progress-bar').attr('style', 'width: ' + (Math.round((itrn + 1) / ids.length * 100)) + '%;').html(progress.replace(/\<i\>/, (itrn + 1)));
593                                 
if(itrn < (ids.length - 1)){
594                                     delete_record(itrn +
1);
595                                 }
else{
596                                     
if(jQuery('.well.details_list li.text-danger, .well.details_list li.text-warning').length){
597                                         jQuery(
'button.details_toggle').removeClass('btn-default').addClass('btn-warning').click();
598                                         jQuery(
'.btn-warning[id^=' + progress_window + '_footer_button_]')
599                                             .toggleClass(
'btn-warning btn-default')
600                                             .html(
'<?php echo addslashes($Translation['ok']); ?>');
601                                     }
else{
602                                         setTimeout(function(){
/* */ jQuery('#' + progress_window).agModal('hide'); }, 500);
603                                     }
604                                 }
605                             }
606                         });
607                     }
608
609                     delete_record(
0);
610                 }
611             },
612             {
613                 label:
'<i class="glyphicon glyphicon-ok"></i> ' + label_no,
614                 bs_class:
'success'
615             }
616         ]
617     });
618 }
619
620 function mass_change_owner(t, ids){
621     
if(ids == undefined) return;
622     
if(!ids.length) return;
623
624     
var update_form = '<?php echo addslashes($Translation['Change owner of <n> selected records to']); ?> ' +
625         
'<span id="new_owner_for_selected_records"></span><input type="hidden" name="new_owner_for_selected_records" value="">';
626     
var confirm_title = '<?php echo addslashes($Translation['Change owner']); ?>';
627     
var label_yes = '<?php echo addslashes($Translation['Continue']); ?>';
628     
var label_no = '<?php echo addslashes($Translation['Cancel']); ?>';
629     
var progress = '<?php echo addslashes($Translation['Updating record <i> of <n>']); ?>';
630     
var continue_updating = true;
631
632     
// request confirmation of mass update operation
633     modal_window({
634         message: update_form.replace(/\<n\>/, ids.length),
635         title: confirm_title,
636         footer: [
/* shows a 'continue' and a 'cancel' buttons .. handler for each follows ... */
637             {
638                 label:
'<i class="glyphicon glyphicon-ok"></i> ' + label_yes,
639                 bs_class:
'success',
640                 
// on confirming, start update operations
641                 click: function(){
642                     
var memberID = jQuery('input[name=new_owner_for_selected_records]').eq(0).val();
643                     
if(!memberID.length) return;
644
645                     
// show update progress, allowing user to abort operations by closing the window or clicking cancel
646                     
var progress_window = modal_window({
647                         title:
'<?php echo addslashes($Translation['Update progress']); ?>',
648                         message:
'' +
649                             
'<div class="progress">' +
650                                 
'<div class="progress-bar progress-bar-success" role="progressbar" style="width: 0;"></div>' +
651                             
'</div>' +
652                             
'<button type="button" class="btn btn-default details_toggle" onclick="' +
653                                 
'jQuery(this).children(\'.glyphicon\').toggleClass(\'glyphicon-chevron-right glyphicon-chevron-down\'); ' +
654                                 
'jQuery(\'.well.details_list\').toggleClass(\'hidden\');'
655                                 +
'">' +
656                                 
'<i class="glyphicon glyphicon-chevron-right"></i> ' +
657                                 
'<?php echo addslashes($Translation['Show/hide details']); ?>' +
658                             
'</button>' +
659                             
'<div class="well well-sm details_list hidden"><ol></ol></div>',
660                         close: function(){
661                             
// stop updating further records ...
662                             continue_updating =
false;
663                         },
664                         footer: [
665                             {
666                                 label:
'<i class="glyphicon glyphicon-remove"></i> <?php echo addslashes($Translation['Cancel']); ?>',
667                                 bs_class:
'warning'
668                             }
669                         ]
670                     });
671
672                     
// begin updating records, one by one
673                     progress = progress.replace(/\<n\>/, ids.length);
674                     
var update_record = function(itrn){
675                         
if(!continue_updating) return;
676                         jQuery.ajax(
'admin/pageEditOwnership.php', {
677                             type:
'POST',
678                             data: {
679                                 pkValue: ids[itrn],
680                                 t: t,
681                                 memberID: memberID,
682                                 saveChanges:
'Save changes'
683                             },
684                             success: function(resp){
685                                 
if(resp == 'OK'){
686                                     jQuery(
".well.details_list ol").append('<li class="text-success"><?php echo addslashes($Translation['record updated']); ?></li>');
687                                     jQuery(
'#record_selector_' + ids[itrn]).prop('checked', false);
688                                     jQuery(
'#select_all_records').prop('checked', false);
689                                 }
else{
690                                     jQuery(
".well.details_list ol").append('<li class="text-danger">' + resp + '</li>');
691                                 }
692                             },
693                             error: function(){
694                                 jQuery(
".well.details_list ol").append('<li class="text-warning"><?php echo addslashes($Translation['Connection error']); ?></li>');
695                             },
696                             complete: function(){
697                                 jQuery(
'#' + progress_window + ' .progress-bar').attr('style', 'width: ' + (Math.round((itrn + 1) / ids.length * 100)) + '%;').html(progress.replace(/\<i\>/, (itrn + 1)));
698                                 
if(itrn < (ids.length - 1)){
699                                     update_record(itrn +
1);
700                                 }
else{
701                                     
if(jQuery('.well.details_list li.text-danger, .well.details_list li.text-warning').length){
702                                         jQuery(
'button.details_toggle').removeClass('btn-default').addClass('btn-warning').click();
703                                         jQuery(
'.btn-warning[id^=' + progress_window + '_footer_button_]')
704                                             .toggleClass(
'btn-warning btn-default')
705                                             .html(
'<?php echo addslashes($Translation['ok']); ?>');
706                                     }
else{
707                                         jQuery(
'button.btn-warning[id^=' + progress_window + '_footer_button_]')
708                                             .toggleClass(
'btn-warning btn-success')
709                                             .html(
'<i class="glyphicon glyphicon-ok"></i> <?php echo addslashes($Translation['ok']); ?>');
710                                     }
711                                 }
712                             }
713                         });
714                     }
715
716                     update_record(
0);
717                 }
718             },
719             {
720                 label:
'<i class="glyphicon glyphicon-remove"></i> ' + label_no,
721                 bs_class:
'warning'
722             }
723         ]
724     });
725
726     
/* show drop down of users */
727     
var populate_new_owner_dropdown = function(){
728
729         jQuery(
'[id=new_owner_for_selected_records]').select2({
730             width:
'100%',
731             formatNoMatches: function(term){
/* */ return '<?php echo addslashes($Translation['No matches found!']); ?>'; },
732             minimumResultsForSearch:
10,
733             loadMorePadding:
200,
734             escapeMarkup: function(m){
/* */ return m; },
735             ajax: {
736                 url:
'admin/getUsers.php',
737                 dataType:
'json',
738                 cache:
true,
739                 data: function(term, page){
/* */ return { s: term, p: page, t: t }; },
740                 results: function(resp, page){
/* */ return resp; }
741             }
742         }).
on('change', function(e){
743             jQuery(
'[name="new_owner_for_selected_records"]').val(e.added.id);
744         });
745
746     }
747
748     populate_new_owner_dropdown();
749 }
750
751 function add_more_actions_link(){
752     window.open(
'https://bigprof.com/appgini/help/advanced-topics/hooks/multiple-record-batch-actions?r=appgini-action-menu');
753 }

754
755 /* detect current screen size (xs, sm, md or lg) */

756 function screen_size(sz){
757     
if(!$j('.device-xs').length){
758         $j(
'body').append(
759             
'<div class="device-xs visible-xs"></div>' +
760             
'<div class="device-sm visible-sm"></div>' +
761             
'<div class="device-md visible-md"></div>' +
762             
'<div class="device-lg visible-lg"></div>'
763         );
764     }
765     
return $j('.device-' + sz).is(':visible');
766 }

767
768 /* enable floating of action buttons
in DV so they are visible on vertical scrolling */
769 function enable_dvab_floating(){
770     
/* already run? */
771     
if(window.enable_dvab_floating_run != undefined) return;
772
773     
/* scroll action buttons of DV on scrolling DV */
774     $j(window).scroll(function(){
775         
if(!screen_size('md') && !screen_size('lg')) return;
776         
if(!$j('.detail_view').length) return;
777
778         
/* get vscroll amount, DV form height, button toolbar height and position */
779         
var vscroll = $j(window).scrollTop();
780         
var dv_height = $j('[id$="_dv_form"]').eq(0).height();
781         
var bt_height = $j('.detail_view .btn-toolbar').height();
782         
var form_top = $j('.detail_view .form-group').eq(0).offset().top;
783         
var bt_top_max = dv_height - bt_height - 10;
784
785         
if(vscroll > form_top){
786             
var tm = parseInt(vscroll - form_top) + 60;
787             
if(tm > bt_top_max) tm = bt_top_max;
788
789             $j(
'.detail_view .btn-toolbar').css({ 'margin-top': tm + 'px' });
790         }
else{
791             $j(
'.detail_view .btn-toolbar').css({ 'margin-top': 0 });
792         }
793     });
794     window.enable_dvab_floating_run =
true;
795 }

796
797 /* check
if a given field's value is unique and reflect this in the DV form */
798 function enforce_uniqueness(table, field){
799     $j(
'#' + field).on('change', function(){
800         
/* check uniqueness of field */
801         
var data = {
802             t: table,
803             f: field,
804             
value: $j('#' + field).val()
805         };
806
807         
if($j('[name=SelectedID]').val().length) data.id = $j('[name=SelectedID]').val();
808
809         $j.ajax({
810             url:
'ajax_check_unique.php',
811             data: data,
812             complete: function(resp){
813                 
if(resp.responseJSON.result == 'ok'){
814                     $j(
'#' + field + '-uniqueness-note').hide();
815                     $j(
'#' + field).parents('.form-group').removeClass('has-error');
816                 }
else{
817                     $j(
'#' + field + '-uniqueness-note').show();
818                     $j(
'#' + field).parents('.form-group').addClass('has-error');
819                     $j(
'#' + field).focus();
820                     setTimeout(function(){
/* */ $j('#update, #insert').prop('disabled', true); }, 500);
821                 }
822             }
823         })
824     });
825 }

826
827 /* persist expanded/collapsed chidren
in DVP */
828 function persist_expanded_child(id){
829     
var expand_these = Cookies.getJSON('Jisort.dvp_expand');
830     
if(expand_these == undefined) expand_these = [];
831
832     
if($j('[id=' + id + ']').hasClass('active')){
833         
if(expand_these.indexOf(id) < 0){
834             
// expanded button and not persisting in cookie? save it!
835             expand_these.push(id);
836             Cookies.
set('Jisort.dvp_expand', expand_these, { expires: 30 });
837         }
838     }
else{
839         
if(expand_these.indexOf(id) >= 0){
840             
// collapsed button and persisting in cookie? remove it!
841             expand_these.splice(expand_these.indexOf(id),
1);
842             Cookies.
set('Jisort.dvp_expand', expand_these, { expires: 30 });
843         }
844     }
845 }

846
847 /* apply expanded/collapsed status to children
in DVP */
848 function apply_persisting_children(){
849     
var expand_these = Cookies.getJSON('Jisort.dvp_expand');
850     
if(expand_these == undefined) return;
851
852     expand_these.each(function(id){
853         $j(
'[id=' + id + ']:not(.active)').click();
854     });
855 }
856
857 function select2_max_width_decrement(){
858     
return ($j('div.container').eq(0).hasClass('theme-compact') ? 99 : 109);
859 }

860
861 /**
862  * @brief AppGini.TVScroll().more() to scroll one column more.
863  * AppGini.TVScroll().less() to scroll one column less.
864  */

865 AppGini.TVScroll = function(){
866
867     
/**
868      * @brief Calculates the width of the first n columns of the TV table
869      *
870      * @param [
in] n how many columns to calculate the width for
871      * @
return Return total width of given n columns, or 0 if n < 1 or invalid
872      */

873     
var _TVColsWidth = function(n){
874         
if(isNaN(n)) return 0;
875         
if(n < 1) return 0;
876
877         
var tw = 0, cc;
878         
for(var i = 0; i < n; i++){
879             cc = $j(
'.table_view .table th:visible').eq(i);
880             
if(!cc.length) break;
881             tw += cc.outerWidth();
882         }
883
884         
return tw;
885     };
886
887     
/**
888      * @brief show/hide tv-scroll buttons based
on whether TV is horizontally scrollable or not
889      * @details should be called once
on document load before hiding TV columns (by calling less())
890      */

891     
var toggle_tv_scroll_tools = function(){
892         
var tr = $j('.table_view .table-responsive'),
893             vpw = tr.width(),
// viewport width
894             tfw = tr.find(
'.table').width(); // full width of the table
895
896         
if(vpw >= tfw) $j('.tv-scroll').parents('.btn-group').hide();
897         
else $j('.tv-scroll').parents('.btn-group').show();
898     }
899
900     
/**
901      * @brief Prepares variables
for use by less & more
902      */

903     
var _TVScrollSetup = function(){
904         
if(AppGini._TVColsScrolled === undefined) AppGini._TVColsScrolled = 0;
905         AppGini._TVColsCount = $j(
'.table_view .table th:visible').length;
906
907         
/* type of scrolling, https://github.com/othree/jquery.rtl-scroll-type */
908         
/*
909             How to interpret AppGini._ScrollType?
910             {LTR | RTL}:{scrollLeft val
for left position}:{scrollLeft val for right position}:{initial scrollLeft val}
911         */

912         
if(AppGini._ScrollType === undefined){
913             
/* all browsers behave the same on LTR */
914             AppGini._ScrollType =
'LTR:0:100:0';
915
916             
if($j('.container').hasClass('theme-rtl')){
917                 
var definer = $j('<div dir="rtl" style="font-size: 14px; width: 4px; height: 1px; position: absolute; top: -1000px; overflow: scroll">ABCD</div>').appendTo('body')[0];
918
919                 AppGini._ScrollType =
'RTL:100:0:0'; // IE
920                 
if(definer.scrollLeft > 0){
921                     AppGini._ScrollType =
'RTL:0:100:70'; // WebKit
922                 }
else{
923                     definer.scrollLeft =
1;
924                     
if(definer.scrollLeft === 0) AppGini._ScrollType = 'RTL:-100:0:0'; // Firefox/Opera
925                 }
926             }
927
928             
/* show/hide #tv-scroll buttons based on TV scroll state */
929             $j(window).resize(toggle_tv_scroll_tools);
930             toggle_tv_scroll_tools();
931         }
932     };
933
934     
/**
935      * @brief Resets all scrolling and setup values.
936      * @details Useful after hiding/showing columns to re-setup TV scrolling
937      */

938     
var reset = function(){
939         
if(AppGini._ScrollType === undefined) return; // nothing to reset!
940         AppGini._TVColsScrolled = undefined;
941
942         
var tr = $j('.table_view .table-responsive');
943         
switch(AppGini._ScrollType){
944             
case 'RTL:100:0:0':
945             
case 'RTL:0:100:0':
946             
case 'RTL:-100:0:0':
947                 tr.scrollLeft(
0);
948                 
break;
949             
case 'RTL:0:100:70':
950                 
var vpw = tr.width(), // viewport width
951                     tfw = tr.find(
'.table').width(); // full width of the table
952                 tr.scrollLeft(tfw - vpw +
10);
953                 
break;
954         }
955
956         _TVScrollSetup();
957     };
958
959     
var _TVScroll = function(){
960         
var scroll = 0,
961             tr = $j(
'.table_view .table-responsive'),
962             cw = _TVColsWidth(AppGini._TVColsScrolled);
// width of columns to scroll to
963
964         
switch(AppGini._ScrollType){
965             
case 'RTL:100:0:0':
966             
case 'LTR:0:100:0':
967                 scroll = cw -
1;
968                 
break;
969             
case 'RTL:-100:0:0':
970                 scroll = -
1 * cw + 1;
971                 
break;
972             
case 'RTL:0:100:70':
973                 
var vpw = tr.width(), // viewport width
974                     tfw = tr.find(
'.table').width(); // full width of the table
975                 scroll = tfw - vpw - cw +
1;
976                 
break;
977         }
978
979         tr.scrollLeft(scroll);
980     };
981
982     
/**
983      * @brief Scroll the TV table
1 column more
984      */

985     
var more = function(){
986         
if(AppGini._TVColsScrolled >= AppGini._TVColsCount) return;
987         AppGini._TVColsScrolled++;
988         _TVScroll();
989     };
990
991     
/**
992      * @brief Scroll the TV table
1 column less
993      */

994     
var less = function(){
995         
if(AppGini._TVColsScrolled <= 0) return;
996         AppGini._TVColsScrolled--;
997         _TVScroll();
998     };
999
1000     _TVScrollSetup();
1001
1002     
return { more: more, less: less, reset: reset };
1003
1004 };
1005
1006 (function($j){
1007     
/*
1008         apply a modal or an
in-page modal to an element,
1009         or access modal methods/events
if it's already 'modal'ed
1010
1011         Expected usage:
1012         
1. $j('any_selector').agModal({ new modal options .. })
1013         
2. $j('#modal_id').agModal('command')
1014         
3. $j('#modal_id').on('event.bs.modal', event_handler)
1015
1016         
case 1: the selector doesn't matter ... the modal will be created and attached
1017                 to the body element .. to retrieve the modal id
if not specified in options:
1018                 
var modal_id = $j('any_selector').agModal({ new modal options .. }).attr('id');
1019
1020         
case 2: the selector must be the modal element .. if it's a standard BS modal,
1021                 command will be passed
as is to .modal() and the return value returned.
1022                 
if it's an in-page modal, command will be emulated and the modal element
1023                 returned.
1024
1025         
case 3: Bootstrap modal events.
1026     */

1027     $j.fn.agModal = function(options){
1028         
var theModal = this,
1029         open = function(){
1030             
return theModal.trigger('show.bs.modal').removeClass('hide').trigger('shown.bs.modal');
1031         },
1032         close = function(){
1033             
return theModal.trigger('hide.bs.modal').addClass('hide').trigger('hidden.bs.modal');
1034         };
1035
1036         
if(typeof(options) == 'string'){
1037             
if(theModal.hasClass('modal')) return theModal.modal(options);
1038             
if(!theModal.hasClass('inpage-modal')) return theModal;
1039
1040             
/* emulate .modal(command) for the in-page modal */
1041             
switch(options){
1042                 
case 'show':
1043                     open();
1044                     
break;
1045                 
case 'hide':
1046                     close();
1047                     
break;
1048             }
1049
1050             
return theModal;
1051         }
1052
1053         
var op = $j.extend({
1054             
/* default options */
1055             id: random_string(
20),
1056             footer: [],
1057             extras: {},
1058             size:
'default',
1059             forceIPM:
false
1060         }, options);
1061
1062         
if(op.url == undefined && op.message == undefined){
1063             console.error(
'Missing message/url in call to AppGini.modal().');
1064             
return theModal;
1065         }
1066
1067         
var iOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent), /* true for iOS devices */
1068         auto_id = (options.id === undefined),
/* true if modal id is auto-generated */
1069
1070         _resize = function(id){
1071             
var mod = $j('#' + id);
1072             
if(!mod.length) return;
1073
1074             
var ipm = (mod.hasClass('inpage-modal') ? '.inpage-modal-' : '.modal-');
1075
1076             
var wh = $j(window).height(),
1077                 mtm = mod.find(ipm +
'dialog').css('margin-top'),
1078                 mhfoh = mod.find(ipm +
'header').outerHeight() + mod.find(ipm + 'footer').outerHeight();
1079
1080             mod.find(ipm +
'dialog').css({
1081                 margin: mtm,
1082                 width:
'calc(100% - 2 * ' + mtm + ')'
1083             });
1084
1085             mod.find(ipm +
'body').css({
1086                 height:
'calc(' + wh + 'px - ' + mhfoh + 'px - 2 * ' + mtm + ' - 6px)'
1087             });
1088         },
1089
1090         _bsModal = function(){
1091             
/* build the html of footer buttons into footer_buttons variable */
1092             
var footer_buttons = '';
1093             
for(i = 0; i < op.footer.length; i++){
1094                 
if(typeof(op.footer[i].label) != 'string') continue;
1095
1096                 op.footer[i] = $j.extend(
1097                     
/* defaults */
1098                     {
1099                         causes_closing:
true,
1100                         bs_class:
'default'
1101                     },
1102                     op.footer[i],
1103                     
/* enforce the following values */
1104                     { id: op.id +
'_footer_button_' + random_string(10) }
1105                 );
1106
1107                 footer_buttons +=
'<button ' +
1108                         
'type="button" ' +
1109                         
'class="btn btn-' + op.footer[i].bs_class + '" ' +
1110                         (op.footer[i].causes_closing ?
'data-dismiss="modal" ' : '') +
1111                         
'id="' + op.footer[i].id + '" ' +
1112                         
'>' + op.footer[i].label +
1113                     
'</button>';
1114             }
1115
1116             
var mod = $j(
1117                 
'<div class="modal fade" tabindex="-1" role="dialog" id="' + op.id + '">' +
1118                     
'<div class="modal-dialog" role="document">' +
1119                         
'<div class="modal-content">' +
1120                             ( op.title != undefined ?
1121                                 
'<div class="modal-header">' +
1122                                     
'<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>' +
1123                                     
'<h4 class="modal-title" style="width: 90%;">' + op.title + '</h4>' +
1124                                 
'</div>'
1125                                 :
''
1126                             ) +
1127                             
'<div class="modal-body">' +
1128                                 ( op.url != undefined ?
1129                                     
'<iframe ' +
1130                                         
'width="100%" height="100%" ' +
1131                                         
'style="display: block; overflow: scroll !important; -webkit-overflow-scrolling: touch !important;" ' +
1132                                         
'sandbox="allow-modals allow-forms allow-scripts allow-same-origin allow-popups" ' +
1133                                         
'src="' + op.url + '">' +
1134                                     
'</iframe>'
1135                                     : op.message
1136                                 ) +
1137                             
'</div>' +
1138                             
'<div class="modal-footer">' + footer_buttons + '</div>' +
1139                         
'</div>' +
1140                     
'</div>' +
1141                 
'</div>'
1142             );
1143
1144             
if(op.url != undefined){
1145                 mod.find(
'.modal-body').css('padding', '0');
1146             }
1147
1148             
return mod;
1149         },
1150
1151         _ipModal = function(){
1152             
/* prepare footer buttons, if any */
1153             
var footer_buttons = '', closer_class = '';
1154             
for(i = 0; i < op.footer.length; i++){
1155                 
if(typeof(op.footer[i].label) != 'string') continue;
1156
1157                 
if(op.footer[i].causes_closing !== false){ op.footer[i].causes_closing = true; }
1158                 op.footer[i].bs_class = op.footer[i].bs_class ||
'default';
1159                 op.footer[i].id = op.id +
'_footer_button_' + random_string(10);
1160
1161                 closer_class = (op.footer[i].causes_closing ?
' closes-inpage-modal' : '');
1162
1163                 footer_buttons +=
'<button type="button" ' +
1164                         
'class="hspacer-lg vspacer-lg btn btn-' + op.footer[i].bs_class + closer_class + '" ' +
1165                         
'id="' + op.footer[i].id + '" ' +
1166                         
'>' + op.footer[i].label + '</button>';
1167             }
1168
1169             
var imc = $j(
1170                 
'<div id="' + op.id + '" ' +
1171                     
'class="inpage-modal hide ' + $j('.container').eq(0).attr('class') + '" ' +
1172                     
'style="' +
1173                         
'padding-left: 0; padding-right: 0;' +
1174                         
'width: 100% !important;' +
1175                     
'">' +
1176                     
'<div ' +
1177                         
'class="inpage-modal-dialog" ' +
1178                         
'style="' +
1179                             
'box-shadow: 0 0 61px 15px #666;' +
1180                             
'margin: 10px !important;' +
1181                             
'border: solid 1px;' +
1182                             
'border-radius: 5px;' +
1183                         
'">' +
1184                         
'<div class="inpage-modal-content">' +
1185                             ( op.title != undefined ?
1186                                 
'<div class="inpage-modal-header" style="border-bottom: solid 1px;">' +
1187                                     
'<div class="row" style="margin: 0;">' +
1188                                         
'<div class="col-xs-10 col-sm-11 inpage-modal-title">' +
1189                                             
'<div class="h4">' + op.title + '</div>' +
1190                                         
'</div>' +
1191                                         
'<div class="col-xs-2 col-sm-1 closes-inpage-modal text-center inpage-modal-dismiss" style="cursor: pointer;">' +
1192                                             
'<h4 class="glyphicon glyphicon-remove"></h4>' +
1193                                         
'</div>' +
1194                                     
'</div>' +
1195                                 
'</div>'
1196                                 :
''
1197                             ) +
1198
1199                             ( footer_buttons.length ?
1200                                 
'<div class="inpage-modal-footer text-right flip" style="border-bottom: solid 1px;">' + footer_buttons + '</div>'
1201                                 :
''
1202                             ) +
1203
1204                             
'<div class="inpage-modal-body">' +
1205                                 ( op.url != undefined ?
1206                                     
'<iframe ' +
1207                                         
'width="100%" height="100%" ' +
1208                                         
'style="display: block; overflow: scroll !important; -webkit-overflow-scrolling: touch !important;" ' +
1209                                         
'sandbox="allow-modals allow-forms allow-scripts allow-same-origin allow-popups" ' +
1210                                         
'src="' + op.url + '">' +
1211                                     
'</iframe>'
1212                                     : op.message
1213                                 ) +
1214                             
'</div>' +
1215                         
'</div>' +
1216                     
'</div>' +
1217                 
'</div>'
1218             );
1219
1220             
/* hover effect for dismiss button + close modal if a closer clicked */
1221             imc.
on('mouseover', '.inpage-modal-dismiss', function(){
1222                 $j(
this).addClass('text-danger bg-danger');
1223             }).
on('mouseout', '.inpage-modal-dismiss', function(){
1224                 $j(
this).removeClass('text-danger bg-danger');
1225             }).
on('click', '.closes-inpage-modal', close);
1226
1227             imc.find(
'.inpage-modal-title').css({
1228                 overflow:
'auto',
1229                 
'white-space': 'nowrap'
1230             });
1231
1232             
return imc;
1233         };
1234
1235         
/* if modal exists, remove it first */
1236         $j(
'#' + op.id).remove();
1237
1238         theModal = ((iOS || op.forceIPM) && op.size ==
'full' ? _ipModal() : _bsModal());
1239
1240         theModal.appendTo(
'body');
1241
1242         
/* bind footer buttons click handlers */
1243         
for(i = 0; i < op.footer.length; i++){
1244             
if(typeof(op.footer[i].click) == 'function'){
1245                 $j(
'#' + op.footer[i].id).click(op.footer[i].click);
1246             }
1247         }
1248
1249         theModal
1250         .
on('show.bs.modal', function(){
1251             
if(op.size != 'full') return;
1252
1253             
/* hide main page to avoid all scrolling/panning hell on touch screens! */
1254             $j(
'.container').eq(0).hide();
1255         })
1256         .
on('shown.bs.modal', function(){
1257             
if(op.size != 'full') return;
1258
1259             
var id = op.id, rsz = _resize;
1260             rsz(id);
1261             $j(window).resize(function(){
/* */ rsz(id); });
1262         })
1263         
//.agModal('show')
1264         .
on('hidden.bs.modal', function(){
1265             
/* display main page again */
1266             
if(op.size == 'full') $j('.container').eq(0).show();
1267
1268             
if(typeof(op.close) == 'function'){
1269                 op.close();
1270             }
1271
1272             
if(!auto_id) return;
1273
1274             
/* if id is automatic, remove modal after 1 minute from DOM */
1275             
var id = op.id;
1276             
var auto_remove = setInterval(function(){
1277                 
if($j('#' + id).is(':visible')) return; // don't remove if visible
1278                 $j(
'#' + id).remove();
1279                 clearInterval(auto_remove);
1280             },
60000);
1281         });
1282
1283         
return theModal;
1284     };
1285 })(jQuery);

1286
1287 /**
1288  * @brief Used
in pages loaded inside modals (e.g. those with Embedded=1) to close the containing modal.
1289  */

1290 AppGini.closeParentModal = function(){
1291     
var pm = window.parent.jQuery(".modal:visible");
1292     
if(!pm.length){
1293         pm = window.parent.jQuery(
".inpage-modal:visible");
1294     }
1295
1296     
if(pm.length) pm.agModal('hide');
1297     
return;
1298 }

1299
1300 /**
1301  * @
return boolean indicating whether a modal is currently open or not
1302  */

1303 AppGini.modalOpen = function(){
/* */
1304     
return jQuery('.modal-dialog:visible').length > 0 || jQuery('.inpage-modal-dialog:visible').length > 0;
1305 };

1306
1307
1308 /**
1309  * @
return true for mobile devices, false otherwise
1310  * @details https://stackoverflow.com/a/
11381730/1945185
1311  */

1312 AppGini.mobileDevice = function(){
/* */
1313     
var check = false;
1314     (function(a){
if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))) check = true;})(navigator.userAgent||navigator.vendor||window.opera);
1315     
return check;
1316 };
1317
1318 AppGini.datetimeFormat = function(datetime){
/* */
1319     
if(undefined == datetime) datetime = 'd';
1320
1321     
var dateFormat = 'MM/DD/YYYY';
1322     
var timeFormat = 'hh:mm:ss A';
1323
1324     
if(datetime.match(/(dt|td)/i)) return dateFormat + ' ' + timeFormat;
1325     
if(datetime.match(/t/i)) return timeFormat;
1326     
return dateFormat;
1327 };
1328
1329 AppGini.hideViewParentLinks = function() {
1330     
/* find and hide parent links if field label has data 'parent_link' set to 'view_parent_hidden' */
1331     $j(
'label[data-parent_link=view_parent_hidden]').each(function(){
1332         $j(
this).parents('.form-group').find('.view_parent').hide();
1333     });
1334 };



Hệ thống xếp lịch học tín chỉ cho sinh viên CNTT trên PHP & MySQL 111.111 lượt xem

Gõ tìm kiếm nhanh...